package io.focus.mijin.zuul.ratelimit.config.properties;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.validation.annotation.Validated;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;

import static java.util.concurrent.TimeUnit.MINUTES;

/**
 * @author Marcos Barbero
 * @author Liel Chayoun
 */
@Data
@Validated
@RefreshScope
@NoArgsConstructor
@ConfigurationProperties(RateLimitProperties.PREFIX)
public class RateLimitProperties {

    public static final String PREFIX = "zuul.ratelimit";

    @Valid
    private Policy defaultPolicy;

    @Valid
    @NotNull
    private List<Policy> defaultPolicyList = Lists.newArrayList();

    @Valid
    @NotNull
    private Map<String, Policy> policies = Maps.newHashMap();

    @Valid
    @NotNull
    private Map<String, List<Policy>> policyList = Maps.newHashMap();

    private boolean behindProxy;

    private boolean enabled;

    @NotNull
    @Value("${spring.application.name:rate-limit-application}")
    private String keyPrefix;

    @Valid
    @NotNull
    private Repository repository = Repository.IN_MEMORY;

    public enum Repository {
        /** 使用REDIS数据存储 */
        REDIS,
        /** 使用ConcurrentHashMap数据存储 */
        IN_MEMORY
    }

    public List<Policy> getPolicies(String key) {
        return policyList.getOrDefault(key, defaultPolicyList);
    }

    @Data
    @NoArgsConstructor
    public static class Policy {

        /**
         * 限制刷新周期(单位: 秒), 默认60秒. 含义: ?秒内只能发起?次请求, 过了这个周期重新计数
         */
        @NotNull
        private Long refreshInterval = MINUTES.toSeconds(1L);

        /**
         * 请求次数限制, 即多长时间内允许?次请求
         */
        private Long limit;

        /**
         * 每次请求的间隔（分种）
         */
        private Long quota;

        /**
         * 限制类型列表, 允许同时配置多种限制类型, 不过一般情况下只会配置一种
         */
        @Valid
        @NotNull
        private List<MatchType> types = Lists.newArrayList();

        @Data
        @NoArgsConstructor
        @AllArgsConstructor
        public static class MatchType {

            @Valid
            @NotNull
            private Type type;

            private String matcher;
        }

        public enum Type {
            /**
             * Rate limit policy considering the authenticated user.
             */
            USER,

            /**
             * 根据请求地址限流
             */
            URL,

            /**
             * 根据IP限流
             */
            IP,

            /**
             * 根据请求参数限流
             */
            PAR
        }
    }
}